package group.rober.saber.jdbc.accessor.impl;

import group.rober.saber.jdbc.DataBoxRowMapper;
import group.rober.saber.jdbc.DefaultNameConverter;
import group.rober.saber.jdbc.NameConverter;
import group.rober.saber.jdbc.accessor.listener.DeleteListener;
import group.rober.saber.jdbc.accessor.listener.InsertListener;
import group.rober.saber.jdbc.accessor.listener.UpdateListener;
import group.rober.saber.jdbc.exception.OptimisticLockException;
import group.rober.saber.kit.*;
import group.rober.saber.lang.DataBox;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Map<String,Object>数据读写
 * Created by tisir<yangsong158@qq.com> on 2017-06-02
 */
public class MapDataAccessor {

    private String table;
    private NamedParameterJdbcTemplate jdbcTemplate;
    private Map<String,String> propertyColumnMap = MapKit.newHashMap();
    private NameConverter nameConverter = new DefaultNameConverter();
    private boolean blankStringReplacedByNull = true;
    private String optimisticLockProperty = null; //乐观锁属性

    public NamedParameterJdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public MapDataAccessor(String table) {
        this.table = table;
    }

    public Map<String, String> getPropertyColumnMap() {
        return propertyColumnMap;
    }

    public void setPropertyColumnMap(Map<String, String> propertyColumnMap) {
        this.propertyColumnMap = propertyColumnMap;
    }

    public NameConverter getNameConverter() {
        return nameConverter;
    }

    public void setNameConverter(NameConverter nameConverter) {
        this.nameConverter = nameConverter;
    }

    public boolean isBlankStringReplacedByNull() {
        return blankStringReplacedByNull;
    }

    public void setBlankStringReplacedByNull(boolean blankStringReplacedByNull) {
        this.blankStringReplacedByNull = blankStringReplacedByNull;
    }

    public String getOptimisticLockProperty() {
        return optimisticLockProperty;
    }

    public void setOptimisticLockProperty(String optimisticLockProperty) {
        this.optimisticLockProperty = optimisticLockProperty;
    }

    private NameConverter nameConverterWrapper;
    protected NameConverter getNameConverterWrapper(){
        if(nameConverterWrapper !=null)return nameConverterWrapper;
        nameConverterWrapper = new NameConverter() {
            public String getPropertyName(String columnName) {
                return null;
            }

            public String getPropertyName(int column) {
                return null;
            }

            public String getColumnName(String propertyName) {
                String column = propertyColumnMap.get(propertyName);
                if(StringKit.isBlank(column)){
                    column = nameConverter.getColumnName(propertyName);
                }
                return column;
            }

            public String getClassName(String tableName) {
                return null;
            }

            public String getTableName(Class<?> clazz) {
                return null;
            }
        };
        return nameConverterWrapper;
    }

    /**
     * 查询数据列表
     * @param sql
     * @param parameters
     * @return
     */
    public List<DataBox> selectList(String sql,Map<String,Object> parameters){
        return jdbcTemplate.query(sql, parameters, new DataBoxRowMapper(nameConverter));
    }

    /**
     * 插入数据对象
     * @param entity
     * @return
     */
    public int insert(Map<String,Object> entity) {
        return insert(entity,null);
    }

    /**
     * 插入数据对象
     * @param entity 数据对象
     * @param listener 插入前后监听器
     * @return
     */
    public int insert(Map<String,Object> entity,InsertListener<Map<String,Object>> listener) {
        if(listener != null)listener.before(entity);

        KeyHolder keyholder=new GeneratedKeyHolder();

        AccessorSQLKit.blankProcess(blankStringReplacedByNull,entity);
        String sql = AccessorSQLKit.genInsertSql(table,entity,optimisticLockProperty,getNameConverterWrapper());
        SqlParameterSource ps=new MapSqlParameterSource(entity);

        //执行
        int r = jdbcTemplate.update(sql,ps,keyholder);
        if(listener != null)listener.after(entity);
        return r;

    }

    /**
     * 根据主键更新数据
     * @param entity 数据对象
     * @param keyAttributes 主键
     * @return
     */
    public int update(Map<String,Object> entity, Map<String, Object> keyAttributes) {
        return update(entity,keyAttributes,null);
    }

    /**
     * 根据主键更新数据
     * @param entity 数据对象
     * @param keyAttributes 主键
     * @param listener 事件监听器
     * @return
     */
    public int update(Map<String,Object> entity, Map<String, Object> keyAttributes, UpdateListener<Map<String,Object>> listener){
        if(listener != null) listener.before(entity);

        AccessorSQLKit.blankProcess(blankStringReplacedByNull,entity);
        String sql = AccessorSQLKit.genUpdateSql(table,entity,keyAttributes,optimisticLockProperty,getNameConverterWrapper());
        MapSqlParameterSource ps=new MapSqlParameterSource(entity);
        ps.addValues(keyAttributes);

        //执行
        int r = jdbcTemplate.update(sql,ps);
        if(r==0){
            throw new OptimisticLockException("SQL[{0}],Parameter:{1}",sql, JSONKit.toJsonString(entity));
        }
        if(listener != null) listener.after(entity);
        return r;

    }


    public int save(Map<String,Object> entity, Map<String, Object> keyAttributes) {
        return save(entity,keyAttributes,null,null);
    }

    public int save(Map<String,Object> entity, Map<String, Object> keyAttributes,InsertListener<Map<String,Object>> insertListener, UpdateListener<Map<String,Object>> updateListener) {
        boolean exists = AccessorSQLKit.exists(jdbcTemplate,table,keyAttributes,getNameConverterWrapper());
        if(exists){
            return update(entity,keyAttributes,updateListener);
        }else{
            return insert(entity,insertListener);
        }
    }

    public int delete(Map<String, Object> keyAttributes) {
        return delete(keyAttributes,null);
    }

    public int delete(Map<String, Object> keyAttributes, DeleteListener<Map<String, Object>> listener) {
        if(listener!=null)listener.before(keyAttributes);

        StringBuffer sql = new StringBuffer();
        sql.append(" DELETE FROM ");
        sql.append(table);
        sql.append(" WHERE ");
        sql.append(AccessorSQLKit.genWhereClause(keyAttributes,nameConverter));

        //执行
        int r = jdbcTemplate.update(sql.toString(),keyAttributes);
        if(listener!=null)listener.after(keyAttributes);
        return r;
    }

    public int insert(List<Map<String,Object>> entityList) {
        return insert(entityList,null);
    }

    public int insert(List<Map<String,Object>> entityList,InsertListener<List<Map<String,Object>>> listener){
        ValidateKit.notEmpty(entityList,"insert data list cannot be empty");
        if(listener != null)listener.before(entityList);

        int ret = 0;
        String sql = null;
        SqlParameterSource[] batchArgs = new SqlParameterSource[entityList.size()];
        for(int i=0;i<entityList.size() ;i++){
            Map<String,Object> entity = entityList.get(i);
            if(sql==null)sql = AccessorSQLKit.genInsertSql(table,entity,optimisticLockProperty,getNameConverterWrapper());
            AccessorSQLKit.blankProcess(blankStringReplacedByNull,entity);
            batchArgs[i]=new MapSqlParameterSource(entity);
        }

        //执行
        int[] r = jdbcTemplate.batchUpdate(sql,batchArgs);
        for(int i=0;i<r.length;i++){
            ret += r[i];
        }
        if(listener != null)listener.after(entityList);
        return ret;

    }

    public int update(List<Map<String,Object>> entityList, List<Map<String, Object>> keyList){
        return update(entityList,keyList,null);
    }

    public int update(List<Map<String,Object>> entityList, List<Map<String, Object>> keyList,UpdateListener<List<Map<String,Object>>> listener) {
        ValidateKit.notEmpty(entityList,"update data list cannot be empty");
        ValidateKit.notEmpty(keyList,"update key attributes cannot be empty");
        ValidateKit.isTrue(entityList.size()==keyList.size(),"data list length must be equal to parameter list length");
        if(listener!=null)listener.before(entityList);

        int ret = 0;
        String sql = null;
        SqlParameterSource[] batchArgs = new SqlParameterSource[entityList.size()];
        for(int i=0;i<entityList.size() ;i++){
            Map<String,Object> entity = entityList.get(i);
            if(sql==null)sql = AccessorSQLKit.genUpdateSql(table,entity,keyList.get(i),optimisticLockProperty,getNameConverterWrapper());
            AccessorSQLKit.blankProcess(blankStringReplacedByNull,entity);
            batchArgs[i]=new MapSqlParameterSource(entity);
        }

        int[] r = jdbcTemplate.batchUpdate(sql,batchArgs);
        for(int i=0;i<r.length;i++){
            ret += r[i];
            if(r[i]==0){
                throw new OptimisticLockException("SQL[{0}],Parameter:{1}",sql, JSONKit.toJsonString(entityList.get(i)));
            }
        }
        if(listener!=null)listener.after(entityList);

        return ret;
    }

    public int save(List<Map<String,Object>> entityList, List<Map<String, Object>> keyList){
        return save(entityList,keyList,null,null);
    }

    public int save(List<Map<String,Object>> entityList, List<Map<String, Object>> keyList,InsertListener<List<Map<String,Object>>> insertListener,UpdateListener<List<Map<String,Object>>> updateListener) {
        ValidateKit.notEmpty(entityList,"update data list cannot be empty");
        ValidateKit.notEmpty(keyList,"update key attributes cannot be empty");
        ValidateKit.isTrue(entityList.size()==keyList.size(),"data list length must be equal to parameter list length");

        List<Integer> updatedIndexList = new ArrayList<Integer>();
        for(int i=0;i<keyList.size();i++){
            boolean exists = AccessorSQLKit.exists(jdbcTemplate,table,keyList.get(i),getNameConverterWrapper());
            if(exists)updatedIndexList.add(i);
        }

        List<Map<String,Object>> insertDataList = new ArrayList<Map<String,Object>>();
        List<Map<String,Object>> updateDataList = new ArrayList<Map<String,Object>>();
        List<Map<String, Object>> updateKeyList = new ArrayList<Map<String, Object>>();

        //把需要插入的部分和更新的部分分出来
        for(int i=0;i<entityList.size();i++){
            if(updatedIndexList.indexOf(i)>=0){
                updateDataList.add(entityList.get(i));
                updateKeyList.add(keyList.get(i));
            }else{
                insertDataList.add(entityList.get(i));
            }
        }

        int ret = 0;
        if(insertDataList.size()>0){
            ret += insert(insertDataList,insertListener);
        }
        if(updateDataList.size()>0){
            ret += update(updateDataList,updateKeyList,updateListener);
        }

        return ret;
    }

    public int delete(List<Map<String, Object>> keyList){
        return delete(keyList,null);
    }

    public int delete(List<Map<String, Object>> keyList,DeleteListener<List<Map<String, Object>>> listener) {
        ValidateKit.notEmpty(keyList,"delete key attributes cannot be empty");
        if(listener != null)listener.before(keyList);

        int ret = 0;
        StringBuffer sql = new StringBuffer();
        SqlParameterSource[] batchArgs = new SqlParameterSource[keyList.size()];
        for(int i=0;i<keyList.size() ;i++){
            if(sql.length()==0){
                sql.append(" DELETE FROM ");
                sql.append(table);
                sql.append(" WHERE ");
                sql.append(AccessorSQLKit.genWhereClause(keyList.get(i),getNameConverterWrapper()));
            }
            batchArgs[i]=new MapSqlParameterSource(keyList.get(i));
        }

        int[] r = jdbcTemplate.batchUpdate(sql.toString(),batchArgs);
        for(int i=0;i<r.length;i++){
            ret += r[i];
        }
        if(listener != null)listener.after(keyList);
        return ret;
    }
}
